#include "cclient.h"
#include "cmaster.h"
#include "cservice.h"

#include <buola/io/cipendpoint.h>
#include <buola/bbox/protocol/cpacket.h>
#include <buola/utility/uuid.h>

namespace buola { namespace bboxd {

static uint64_t sNextID=1;

CClient::CClient(CMaster *pMaster,io::PSocket pSocket)
    :   CAsyncConnection(std::move(pSocket),pMaster->UUID())
    ,   mMaster(pMaster)
    ,   mUID(sNextID++)
{
    msg_info() << "registering client\n";
    mMaster->RegisterClient(this);
}

CClient::~CClient()
{
    msg_info() << "unregistering client\n";
    mMaster->UnregisterClient(this);
}

void CClient::SendClientConnection(io::PSocket pSocket,uint64_t pOther)
{
    using namespace bbox::protocol;
    
    CStackPacket lPacket(io::nHeader,EOpcode::CLIENTCONNECTION);

    lPacket << pOther;
    lPacket.SetPassSocket(std::move(pSocket));
    msg_info() << "sending client connection\n";
    SendPacket(lPacket);
}

void CClient::DoRegisterService(bbox::protocol::CPacket &pPacket)
{
    std::string lAddress;
    uint64_t lUID;
    pPacket >> lAddress >> lUID;
    msg_info() << "registering service " << lUID << " at " << lAddress << "\n";
    if(mMaster->RegisterService(lAddress,this,lUID))
    {
        msg_info() << "ok\n";
        bbox::protocol::CReplyPacket lReply(pPacket);
        SendPacket(lReply);
    }
    else
    {
        msg_info() << "address in use\n";
        SendError(pPacket,bbox::protocol::EError::ADDRESS_IN_USE);
    }
}

void CClient::DoUnregisterService(bbox::protocol::CPacket &pPacket)
{
    msg_info() << "unregistering service\n";
    std::string lAddress;
    pPacket >> lAddress;
    mMaster->UnregisterService(lAddress,this);
    bbox::protocol::CReplyPacket lReply(pPacket);
    SendPacket(lReply);
}

void CClient::DoGetService(bbox::protocol::CPacket &pPacket)
{
    msg_info() << "getting service\n";
    std::string lAddress;
    pPacket >> lAddress;
    msg_info() << "  address:" << lAddress << "\n";
    CService *lService=mMaster->GetService(lAddress);
    if(!lService)
    {
        SendError(pPacket,bbox::protocol::EError::SERVICE_NOT_FOUND);
        return;
    }
    
    bbox::protocol::CReplyPacket lReply(pPacket);
    lReply << lService->Client()->UID() << lService->UID();
    SendPacket(lReply);
}

void CClient::DoConnectToClient(bbox::protocol::CPacket &pPacket)
{
    msg_info() << "connect to client!\n";
    uint64_t lClientID;
    pPacket >> lClientID;

    //it is a local peer
    CClient* lClient=mMaster->GetClient(lClientID);

    if(!lClient)
    {
        msg_error() << "client not found in ConnectToClient\n";
        SendError(pPacket,bbox::protocol::EError::CLIENT_NOT_FOUND);
        return;
    }
        
    auto lSockets=io::socket_pair(io::ESocketType::PACKET);
    
    lClient->SendClientConnection(std::move(lSockets.first),mUID);
    
    bbox::protocol::CReplyPacket lReply(pPacket);
    lReply.SetPassSocket(std::move(lSockets.second));
    SendPacket(lReply);
}

void CClient::OnPacket(bbox::protocol::CPacket &pPacket)
{
    using namespace bbox::protocol;

    switch(pPacket.Header().Major())
    {
    case EOpcode::REGISTERSERVICE:
        DoRegisterService(pPacket);
        break;
    case EOpcode::UNREGISTERSERVICE:
        DoUnregisterService(pPacket);
        break;
    case EOpcode::GETSERVICE:
        DoGetService(pPacket);
        break;
    case EOpcode::CONNECTTOCLIENT:
        DoConnectToClient(pPacket);
        break;
    default:
        msg_warn() << "received unknown packet with opcode=" << (int)pPacket.Header().Major() << "... ignoring it\n";
    }
}

void CClient::OnReply(bbox::protocol::CPacket& pPacket)
{

}

void CClient::OnEOF()
{
    delete this;
}

void CClient::OnHello(const std::string &pPeerName)
{
    mName=pPeerName;
}

/*namespace bboxd*/ } /*namespace buola*/ }
